//---------------------------------------------------------------------------

#include <vcl.h>
#include <DBTables.hpp>
#pragma hdrstop

#include "uBDETableOp.h"
#include "Eic.h"
#include "MTAG_EIC.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)


#define MODULE_TAG 2

namespace BDETableOperation
{
/*----------------------------------------------------------------------------
------------- GetBDEDescs() --------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette procédure retourne diverses descriptions BDE de la table.           -
-                                                                              -
- Paramètres :                                                                 -
-   szFileName  : Nom de la table
-   hdb         : Retourne le handle de la base de données associée à la table.-
-   tbldesc     :    "     la description BDE minimale de la table.            -
-   PropsTbl    : Si != NULL en entrée, retourne les propriétés BDE de la table.
-   ppvchkDescs : Si != NULL en entrée, retourne un tableau (alloué avec une   -
-                 entrée en plus) de tous les contrôles de validité de la table-
-                                                                              -
- Notes : * La table doit être fermée.                                         -
-         * Les pointeurs passés en paramètre doivent être à NULL.             -
-
- ATTENTION : Cette fonction fait des allocations qui devront être désalloué par
              la fonction appelante.
              Les variables concernées sont :
              - tbldesc->pfldDesc[]
              - tbldesc->pecrFldOp[]
              - ppvchkDescs[]
\*----------------------------------------------------------------------------*/
void __fastcall GetBDEDescs( const AnsiString &aFileName,
                             hDBIDb*    hdb,
                             CRTblDesc* tbldesc,
                             CURProps*  PropsTbl /* = NULL */,
                             VCHKDesc** ppvchkDescs /* = NULL*/)
{
 TTable    *Table = NULL;
 Word      wNbFieldsP1; // Nb de champs de la table + 1
 EICTag    tag;
 Word      wTmp;        // Variables de boucle
 bool      bPropsTblIsAlloue = false;

 ASSERT(!ppvchkDescs || !*ppvchkDescs);
 try {
   try {
     Table = new TTable(NULL);
     Table->TableName = aFileName;

     memset(tbldesc, 0, sizeof(*tbldesc));

     if (!PropsTbl)
        {
        PropsTbl = new CURProps;
        bPropsTblIsAlloue = true;
        }

     // => Ouverture de la table
     tag = TAG( 10); Table->Open();
     // => Lecture de la description de tous les champs actuels de la table
     tag = TAG( 12); Dbtables::Check(Bde::DbiGetCursorProps(Table->Handle, *PropsTbl));
     strcpy(PropsTbl->szName, aFileName.c_str());
     tag = TAG( 13); tbldesc->pfldDesc = new FLDDesc[wNbFieldsP1 = (Word)(PropsTbl->iFields + 1)];
     tag = TAG( 14); Dbtables::Check( Bde::DbiGetFieldDescs(Table->Handle, tbldesc->pfldDesc));

     // => Lecture éventuelle des contrôles de validité des champs
     if( ppvchkDescs)
     {
       VCHKDesc* pvchkDesc; // Un contrôle de validité de la table

       tag = TAG( 15); *ppvchkDescs = pvchkDesc = new VCHKDesc[PropsTbl->iValChecks + 1];
       for( tag = TAG( 16), wTmp = 0; wTmp < PropsTbl->iValChecks; pvchkDesc++)
         Dbtables::Check( Bde::DbiGetVchkDesc(Table->Handle, ++wTmp, pvchkDesc));
       memset( pvchkDesc, 0, sizeof( *pvchkDesc)); // RAZ du dernier contrôle (celui alloué en plus)
     }

     // => Sauvegarde du handle de la base de données associée à la table
     tag = TAG( 17); *hdb = Table->DBHandle;
     // => Fermeture de la table
     tag = TAG( 18); Table->Close();
     // => Création et initialisation des opérateurs sur les champs
     //    ET Synchronisation de l'ordre des champs existant (par indiçage croissant)

     tag = TAG( 19); tbldesc->pecrFldOp = new CROpType[ wNbFieldsP1];
     tag = TAG( 20);
     for( wTmp = 0; wTmp < wNbFieldsP1; wTmp++)
       {
       tbldesc->pecrFldOp[wTmp] = crNOOP;
       tbldesc->pfldDesc[wTmp].iFldNum = wTmp+1;
       }

     // => Description de la table
     tag = TAG( 21);
     strcpy(tbldesc->szTblName, PropsTbl->szName);
     strcpy(tbldesc->szTblType, PropsTbl->szTableType);
     tbldesc->iFldCount  = PropsTbl->iFields;
   } catch( Exception& e) {
     Table->Close();
     throw EUser( tag, e);
   }
 }
 __finally
  {
  delete Table;     Table = NULL;
  if (bPropsTblIsAlloue)
     {
     delete PropsTbl;
     PropsTbl = NULL;
     }
  }
}


/*******************************************************************************

Fonction :

Paramètres d'entrée :

Paramètres de sortie :

Valeur retournée :
*******************************************************************************/
DWORD __fastcall ConvertTFieldTypeToefldPDXType (TFieldType Type)
{
try
   {
   switch(Type)
      {
      case ftString:
         return fldPDXCHAR;
      case ftFloat:
         return fldPDXNUM;
      case ftCurrency:
         return fldPDXMONEY;
      case ftDate:
         return fldPDXDATE;
      case ftWord:
      case ftSmallint:
         return fldPDXSHORT;
      case ftMemo:
         return fldPDXMEMO;
//      case CORRESPONDANCE ?:
//         return fldPDXBINARYBLOB; A quoi ça correspond ?
      case ftFmtMemo:
         return fldPDXFMTMEMO;
//      case CORRESPONDANCE ?:
//         return fldPDXOLEBLOB; A quoi ça correspond ?
      case ftGraphic:
         return fldPDXGRAPHIC;
      case ftBlob:
         return fldPDXBLOB;
      case ftInteger:
         return fldPDXLONG;
      case ftTime:
         return fldPDXTIME;
      case ftDateTime:
         return fldPDXDATETIME;
      case ftBoolean:
         return fldPDXBOOL;
      case ftAutoInc:
         return fldPDXAUTOINC;
      case ftBytes:
         return fldPDXBYTES;
      case ftBCD:
         return fldPDXBCD;
//      case CORRESPONDANCE ?:
//         return fldPDXUNICHAR; A quoi ça correspond ?
      case ftUnknown:
      case ftVarBytes:
      case ftParadoxOle:
      case ftDBaseOle:
      case ftTypedBinary:
      case ftCursor:
      case ftFixedChar:
      case ftWideString:
      case ftLargeint:
      case ftADT:
      case ftArray:
      case ftReference:
      case ftDataSet:
      case ftOraBlob:
      case ftOraClob:
      case ftVariant:
      case ftInterface:
      case ftIDispatch:
      case ftGuid:
      default :
         ASSERT(false);
         return 0;
      }
   }
catch(Exception &e)
   {
   EICDisplayException(TAG(20), e);
   }
return 0;
}

/*******************************************************************************

Fonction :

Paramètres d'entrée :

Paramètres de sortie :

Valeur retournée :
*******************************************************************************/
short __fastcall Convert_fldPDXTofld(short TypePDX)
{
try
   {
   switch(TypePDX)
      {
      case fldPDXCHAR:
         return fldZSTRING;
      case fldPDXNUM:
         return fldFLOAT;
      case fldPDXMONEY:
         ASSERT(false); //On retourne quoi ?
         return 0 ;
      case fldPDXDATE:
         return fldDATE;
      case fldPDXSHORT:
         return fldINT16;
      case fldPDXBLOB: //(ALIAS de fldPDXMEMO)
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXBINARYBLOB:
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXFMTMEMO:
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXOLEBLOB:
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXGRAPHIC:
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXLONG:
         return fldINT32;
      case fldPDXTIME:
         return fldTIME;
      case fldPDXDATETIME:
         return fldTIMESTAMP;
      case fldPDXBOOL:
         return fldBOOL;
      case fldPDXAUTOINC:
         ASSERT(false); //On retourne quoi ?
         return 0;
      case fldPDXBYTES:
         return fldBYTES;
      case fldPDXBCD:
         return fldBCD;
      case fldPDXUNICHAR:
         ASSERT(false); //On retourne quoi ?
         return 0;
      default:
         ASSERT(false); //Type inconnu
      }
   }
catch(Exception &e)
   {
   EICDisplayException(TAG(30), e);
   }
return 0;
}

/*******************************************************************************
void __fastcall InsertField(short shPosInsertion,
                            char szFieldName[],
                            short shTypeFieldParadox,
                            unsigned short ushMaxStringLen,
                            bool           bObligatoire   ,
                            const AnsiString &aDefaultValue,
                            CRTblDesc *tblDesc)

Fonction : Ajoute un champ vierge dans la structure de la table.
           S'il a déjà été créé, on ne fait rien.
           Initialise ses propriétés szName (Nom du champ),
                                     iFldNum (Numéro de champ),
                                     iFldType (Type de champ)
           On peut ensuite accéder à la description du nouveau champ par
           pfldBase[shPosInsertion-1]

Paramètres d'entrée : shPosInsertion : Position d'insertion (Base 1)
                      szFieldName : Nom du champ à ajouter
                      shTypeFieldParadox : Type de champ à ajouter (fldPDX...)
                      ushMaxStringLen : Nombre maximum de caractères (Ignoré si shTypeFieldParadox != fldPDXCHAR)
                      aDefaultValue : Valeur par défaut

Paramètres modifiés : tblDesc : Description de la table
*******************************************************************************/
void __fastcall InsertField(short shPosInsertion,
                            char szFieldName[],
                            short shTypeFieldParadox,
                            unsigned short ushMaxStringLen,
                            bool           bObligatoire   ,
                            const AnsiString &aDefaultValue,
                            CRTblDesc *tblDesc)
{
FLDDesc *pfldNouvelleBase = NULL;
CROpType *pNouvelleOpsChamps = NULL;
VCHKDesc	*pNouveauCtrlVal = NULL;
CROpType *pNouvelleOpsCtrlVal = NULL;
int i;
short shTypeField;

try
   {
   ASSERT(tblDesc);
   ASSERT(shPosInsertion <= tblDesc->iFldCount+1); //On ne peut insérer après le "futur" dernier champ

   shTypeField = Convert_fldPDXTofld(shTypeFieldParadox);

   for (i=0;
           i<tblDesc->iFldCount && strcmpi(szFieldName, tblDesc->pfldDesc[i].szName) != 0;
               i++);

   if (i < tblDesc->iFldCount) //SI le champ à ajouter existe déjà
      {
      //SI le type du champ existant correspond au type désiré
      // => On ne fait rien de plus
      if (tblDesc->pfldDesc[i].iFldType == shTypeField)
         return;
      else
         throw EAbort("Impossible d'ajouter le champ "+AnsiString(szFieldName)+ " à la table.\n"
                      "Ce champ existe déjà");
      }

   pfldNouvelleBase = new FLDDesc [tblDesc->iFldCount + 1];
   memset(pfldNouvelleBase, 0, sizeof(*pfldNouvelleBase) * (tblDesc->iFldCount + 1));
   pNouvelleOpsChamps = new CROpType[tblDesc->iFldCount + 1];
   memset(pNouvelleOpsChamps, 0, sizeof(*pNouvelleOpsChamps) * (tblDesc->iFldCount + 1));

   //On transfert le descriptif des champs de l'ancien tableau vers le nouveau tableau
   //avant le nouveau champ
   for (i=0 ; i<shPosInsertion-1 ; i++)
      {
      pfldNouvelleBase[i] = tblDesc->pfldDesc[i];
      pNouvelleOpsChamps[i] = tblDesc->pecrFldOp[i];
      }

   //On ajoute le nouveau champ
   strcpy(pfldNouvelleBase[i].szName, szFieldName);
   pfldNouvelleBase[i].iFldNum = tblDesc->iFldCount+1;
   pfldNouvelleBase[i].iFldType = shTypeField;
   // Champ chaîne  => Initialisation de la longueur max du champ
   if(shTypeField == fldZSTRING)
      pfldNouvelleBase[i].iUnits1 = ushMaxStringLen;
   pNouvelleOpsChamps[i] = crADD;

   // => Adjonction éventuelle du contrôle de validité sur le nouveau champ
   if(bObligatoire || !aDefaultValue.IsEmpty())
      {
      pNouvelleOpsCtrlVal = new CROpType [tblDesc->iValChkCount+1];
      memcpy(pNouvelleOpsCtrlVal, tblDesc->pecrValChkOp, sizeof(*tblDesc->pecrValChkOp)*(tblDesc->iValChkCount));
      pNouvelleOpsCtrlVal[tblDesc->iValChkCount] = crADD;

      pNouveauCtrlVal = new VCHKDesc [tblDesc->iValChkCount+1];
      memcpy(pNouveauCtrlVal, tblDesc->pvchkDesc, sizeof(*tblDesc->pvchkDesc)*(tblDesc->iValChkCount));
      memset(&pNouveauCtrlVal[tblDesc->iValChkCount], 0, sizeof(pNouveauCtrlVal[tblDesc->iValChkCount]));

      pNouveauCtrlVal[tblDesc->iValChkCount].iFldNum = pfldNouvelleBase[i].iFldNum;
      pNouveauCtrlVal[tblDesc->iValChkCount].bRequired = bObligatoire;

      if(!aDefaultValue.IsEmpty())
         {
         pNouveauCtrlVal[tblDesc->iValChkCount].bHasDefVal = true;
         memcpy(pNouveauCtrlVal[tblDesc->iValChkCount].aDefVal, aDefaultValue.c_str(), aDefaultValue.Length());
         }
      }

   //On transfert le descriptif des champs de l'ancien tableau vers le nouveau tableau
   //après le nouveau champ
   for (i=shPosInsertion ; i<tblDesc->iFldCount+1 ; i++)
      {
      pfldNouvelleBase[i] = tblDesc->pfldDesc[i-1];

      if (tblDesc->pecrFldOp[i-1] == crNOOP)
         pNouvelleOpsChamps[i] = crMODIFY;
      else
         pNouvelleOpsChamps[i] = tblDesc->pecrFldOp[i-1];
      }

   delete []tblDesc->pfldDesc;   tblDesc->pfldDesc = NULL;
   delete []tblDesc->pecrFldOp;  tblDesc->pecrFldOp = NULL;

   tblDesc->pfldDesc  = pfldNouvelleBase;
   tblDesc->pecrFldOp = pNouvelleOpsChamps;
   tblDesc->iFldCount++;

   //Si des contrôles de validité ont été ajoutées 
   if (pNouvelleOpsCtrlVal)
      {
      delete []tblDesc->pvchkDesc;          tblDesc->pvchkDesc    = NULL;
      delete []tblDesc->pecrValChkOp;       tblDesc->pecrValChkOp = NULL;
      tblDesc->pvchkDesc = pNouveauCtrlVal;
      tblDesc->pecrValChkOp = pNouvelleOpsCtrlVal;
      tblDesc->iValChkCount++;
      }
   }
catch(Exception &e)
   {
   throw EUser(TAG(40), e);
   }
}

/*******************************************************************************
void __fastcall DeleteField(char *szFieldName, CRTblDesc *tblDesc)

Fonction : Supprime un champ dans la structure de la table.

Paramètres d'entrée : szFieldName : Nom du champ à supprimé

Paramètres modifiés : tblDesc : Description de la table

Valeur retournée :
*******************************************************************************/
void __fastcall DeleteField(char *szFieldName, CRTblDesc *tblDesc)
{
FLDDesc *pfldNouvelleBase;
CROpType *pNouvelleOps;
int i, j;
int iChampASuppr;

try
   {
   ASSERT(tblDesc);
   ASSERT(szFieldName);

   //On cherche l'indice du champ à supprimer
   for (iChampASuppr=0;
            iChampASuppr<tblDesc->iFldCount && strcmpi(szFieldName, tblDesc->pfldDesc[iChampASuppr].szName) != 0;
               iChampASuppr++);

   //SI le champ à supprimer n'existe pas
   if (iChampASuppr == tblDesc->iFldCount)
      return;

   pfldNouvelleBase = new FLDDesc [tblDesc->iFldCount - 1];
   memset(pfldNouvelleBase, 0, sizeof(*pfldNouvelleBase) * (tblDesc->iFldCount - 1));
   pNouvelleOps     = new CROpType[tblDesc->iFldCount - 1];
   memset(pNouvelleOps, 0, sizeof(*pNouvelleOps) * (tblDesc->iFldCount - 1));

   //On transfert le descriptif des champs de l'ancien tableau vers le nouveau tableau
   //excepté le champ à supprimer
   j=0;
   for (i=0; i<tblDesc->iFldCount ; i++)
      if (i != iChampASuppr)
         {
         pfldNouvelleBase[j] = tblDesc->pfldDesc[i];
         pNouvelleOps[j] = tblDesc->pecrFldOp[i];
         j++;
         }

   delete []tblDesc->pfldDesc;   tblDesc->pfldDesc = NULL;
   delete []tblDesc->pecrFldOp;  tblDesc->pecrFldOp = NULL;

   tblDesc->pfldDesc  = pfldNouvelleBase;
   tblDesc->pecrFldOp = pNouvelleOps;
   tblDesc->iFldCount--;
   }
catch(Exception &e)
   {
   throw EUser(TAG(50), e);
   }
}


/*******************************************************************************

Fonction :

Paramètres d'entrée :

Paramètres de sortie :

Valeur retournée :
*******************************************************************************/
bool __fastcall FieldExist(const char *szFieldName, const CRTblDesc &tblDesc)
{
try
   {
   for (int i=0 ; i<tblDesc.iFldCount ; i++)
      if (stricmp(tblDesc.pfldDesc[i].szName, szFieldName) == 0)
         return true;
   }
catch(Exception &e)
   {
   EICDisplayException(TAG(60), e);
   }
return false;
}

/*******************************************************************************
void __fastcall ChangeFieldType(const char *szFieldName,
                                Word           efldPDXType,   // Une valeur fldPDX* restreinte
                                unsigned short ushMaxDataLen,
                                const CRTblDesc *tblDesc)

Fonction : Change le type du champ spécifié dans le descripteur de table.

Paramètres d'entrée : szFieldName   : Nom du champ à modifier (caractères OEM)
                      efldPDXType   : Nouveau type du champ (une valeur restreinte fldPDX*)
                      ushMaxDataLen : Longueur maximale des données du champ (pour fldPDXMEMO et
                                      fldPDXCHAR seulement)
Paramètres modifié : tblDesc : Descripteur de champ

*******************************************************************************/
void __fastcall ChangeFieldType(const char *szFieldName,
                                Word           efldPDXType,   // Une valeur fldPDX* restreinte
                                unsigned short ushMaxDataLen,
                                CRTblDesc *tblDesc)
{
int iIndiceChamp;

try
   {
   for (iIndiceChamp=0 ; iIndiceChamp<tblDesc->iFldCount && stricmp(tblDesc->pfldDesc[iIndiceChamp].szName, szFieldName) != 0; iIndiceChamp++);

   //Le champ n'existe pas.
   if (iIndiceChamp == tblDesc->iFldCount)
      throw EAbort("Impossible de changer le type du champ "+AnsiString(szFieldName)+ ".\n"
                   "Le champ n'existe pas");

   ASSERT(tblDesc->pfldDesc[iIndiceChamp].efldvVchk == fldvNOCHECKS); // Changement de type de champ avec contrôle de validité non implémenté !!!

   if (tblDesc->pfldDesc[iIndiceChamp].iFldType != efldPDXType)
      {
      tblDesc->pfldDesc[iIndiceChamp].iFldType = efldPDXType;
      tblDesc->pfldDesc[iIndiceChamp].iUnits1  = ushMaxDataLen;
      tblDesc->pecrFldOp[iIndiceChamp]         = crMODIFY;
      }
   }
catch(Exception &e)
   {
   EICDisplayException(TAG(70), e);
   }
}

/*******************************************************************************

Fonction :

Paramètres d'entrée :

Paramètres de sortie :

Valeur retournée :
*******************************************************************************/
void __fastcall RenameField(char *szNomsource, char *szNomCible, CRTblDesc *tblDesc)
{
int iIndiceChamp;
try
   {
   for (iIndiceChamp=0 ; iIndiceChamp<tblDesc->iFldCount && stricmp(tblDesc->pfldDesc[iIndiceChamp].szName, szNomsource) != 0; iIndiceChamp++);

   //Le champ n'existe pas.
   if (iIndiceChamp == tblDesc->iFldCount)
      {
      for (iIndiceChamp=0 ; iIndiceChamp<tblDesc->iFldCount && stricmp(tblDesc->pfldDesc[iIndiceChamp].szName, szNomCible) != 0; iIndiceChamp++);

      //Le champ cible n'existe pas non plus
      if (iIndiceChamp == tblDesc->iFldCount)
         throw EAbort("Impossible de renommer le champ "+ AnsiString(szNomsource)+ ".\n"
                      "Le champ n'existe pas");
      else //Champ déjà renommé
         return;
      }

   strcpy(tblDesc->pfldDesc[iIndiceChamp].szName, szNomCible);
   tblDesc->pecrFldOp[iIndiceChamp] = crMODIFY;
   }
catch(Exception &e)
   {
   throw EUser(TAG(80), e);
   }
}

/*******************************************************************************
bool PackTable (AnsiString RepTable, AnsiString NomTable);

Fonction : Réduit la taille des tables en effacant toutes les données inutiles.

P.E : RepTable : Répertoire contenant le table.
      NomTable : Nom de la table.

P.S. : false si erreur sinon true

*******************************************************************************/
bool PackTable (const AnsiString &RepTable, const AnsiString &NomTable)
   {
   DBIEnv    Env;
   hDBIDb    hDb = NULL;
   CRTblDesc TblDesc;

   try
      {
      ///   Initialisation du Dbi
      /////////////////////////////////////////////////////////////////////////////
      memset(&Env, 0, sizeof(DBIEnv));

      strcpy(Env.szWorkDir, RepTable.c_str());
      Check(DbiInit(&Env));

      ///   Ouverture d'une base
      /////////////////////////////////////////////////////////////////////////////
      Check(DbiOpenDatabase(NULL, NULL, dbiREADWRITE, dbiOPENSHARED,
                                  NULL, 0, NULL, NULL, hDb));
      Check(DbiSetDirectory(hDb, RepTable.c_str()));

      ///   Nettoyage de la table
      /////////////////////////////////////////////////////////////////////////////
      memset((void *) &TblDesc, 0, sizeof(CRTblDesc));
      lstrcpy(TblDesc.szTblName, NomTable.c_str());
      lstrcpy(TblDesc.szTblType, szPARADOX);
      TblDesc.bPack = TRUE;
      Check(DbiDoRestructure(hDb, 1, &TblDesc, NULL, NULL, NULL, FALSE));
      }
   catch(Exception &e)
      {
      EICDisplayException(TAG(90), e);
      return false ;
      }
   return true ;
   }

} // namespace BDETableOperation
